﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.XPath;
using Authorit.API;
using AuthoritExtensibility;

namespace Authorit.API.Utilities
{
    #region PluginUtility
    /// <summary>
    /// The <see cref="PluginUtility"/> static class implements methods that return information about an Author-it library.
    /// </summary>
    /// <remarks>You use the <see cref="PluginUtility"/> static class to assist in implementing an Author-it plug-in.</remarks>
    public static class PluginUtility
    {
        #region Fields (1) 

        /// <summary>
        /// The <see cref="htmlRegex"/> field stores a compiled regular expression used for
        /// removing HTML tags from text.
        /// </summary>
        /// <seealso cref="GetTopicText(int, Authorit.API.Services.Topic)"/>
        private static Regex htmlRegex = new Regex("<.*?>", RegexOptions.Compiled);
        #endregion Fields 

        #region Methods (11) 

        // Public Methods (10) 

        /// <summary>
        /// The <see cref="GetJetLibraryConfiguration(AuthoritEventArgs)"/> method gets a Jet library configuration.
        /// </summary>
        /// <param name="args">An instance of the <see cref="AuthoritEventArgs"/> class that contains a value in the <see cref="AuthoritEventArgs.LibraryPath"/> property.</param>
        /// <returns>An instance of the <a href="http://devhub.author-it.com/Library/#b4293t2520n8120" target="_blank"><see cref="API.Configuration.JetLibraryConfiguration"/></a> class for the Jet library specified by the <paramref name="args"/> parameter.</returns>
        /// <exception cref="ArgumentException">Thrown when the <a href="http://devhub.author-it.com/Library/#b4291t4078n6777" target="_blank"><see cref="AuthoritEventArgs.LibraryPath"/></a> property of the <paramref name="args"/> parameter is <value>null</value></exception>
        /// <exception cref="FileNotFoundException">Thrown when the jet library specified in the <see cref="AuthoritEventArgs.LibraryPath"/> property of the <paramref name="args"/> parameter does not exist</exception>
        /// <seealso cref="GetLibraryConfigurationList(AuthoritEventArgs)"/>
        /// <remarks>Use the <see cref="GetJetLibraryConfiguration(AuthoritEventArgs)"/> method when you know the library specified by <paramref name="args"/> is a <see cref="LibraryType.Jet"/> library. Use the <see cref="GetLibraryType(AuthoritEventArgs)"/> method to determine the library type.</remarks>
        public static API.Configuration.JetLibraryConfiguration GetJetLibraryConfiguration(AuthoritEventArgs args)
        {

            if (null == args.LibraryPath)
            {
                throw new ArgumentException("args", "LibraryPath property cannot be null");
            }
            Configuration.JetLibraryConfiguration jetConfig = new Authorit.API.Configuration.JetLibraryConfiguration();

            System.IO.FileInfo jetFileInfo = new FileInfo(args.LibraryPath);
            if (jetFileInfo.Exists)
            {
                jetConfig.Alias = jetFileInfo.Name;
                jetConfig.Path = jetFileInfo.FullName;
            }
            else
            {
                string notFoundMessage = String.Format("Jet library {0} not found in path {1}", jetFileInfo.Name, jetFileInfo.DirectoryName);
                throw new FileNotFoundException(notFoundMessage, jetFileInfo.FullName);
            }
            return jetConfig;
        }

        /// <summary>
        /// The <see cref="GetLibraryConfiguration(AuthoritEventArgs)"/> static method gets a new library configuration that contains information for the specified library connection.
        /// </summary>
        /// <param name="args">An instance of the <see cref="AuthoritEventArgs"/> class that contains library connection information</param>
        /// <returns>A new instance of the <a href="http://devhub.author-it.com/Library/#b4293t2522n8130" target="_blank"><see cref="API.Configuration.LibraryConfigurationBase"/></a> class containing library information specified by the <paramref name="args"/></returns>
        /// <remarks>The <see cref="GetLibraryConfiguration(AuthoritEventArgs)"/> method returns <value>null</value> if the method cannot determine the library type.</remarks>
        public static API.Configuration.LibraryConfigurationBase GetLibraryConfiguration(AuthoritEventArgs args)
        {
            API.Configuration.LibraryConfigurationBase libConfig;
            libConfig = null;
            switch (GetLibraryType(args))
            {
                case LibraryType.Unknown:
                    break;
                case LibraryType.Jet:
                    libConfig = GetJetLibraryConfiguration(args);
                    break;
                case LibraryType.Sql:
                    libConfig = GetSqlLibraryConfiguration(args);
                    break;
                default:
                    break;
            }
            return libConfig;
        }

        /// <summary>
        /// The <see cref="GetLibraryConfigurationList(AuthoritEventArgs)"/> static method gets a new library configuration list that contains information for the specified library connection.
        /// </summary>
        /// <param name="args">An instance of the <see cref="AuthoritEventArgs"/> class that contains library connection information</param>
        /// <returns>A new instance of the <a href="http://devhub.author-it.com/Library/#b4293t2524n8138" target="_blank"><see cref="API.Configuration.LibraryConfigurationList"/></a> class containing library information specified by the <paramref name="args"/> parameter</returns>
        /// <remarks>Use the <see cref="GetLibraryConfigurationList(AuthoritEventArgs)"/> method to get a new instance of the <see cref="API.Configuration.LibraryConfigurationList"/> class with one member.
        /// To add members to an existing instance of <see cref="API.Configuration.LibraryConfigurationList"/> use the <see cref="GetLibraryConfiguration(AuthoritEventArgs)"/> method and add the return value.</remarks>
        public static API.Configuration.LibraryConfigurationList GetLibraryConfigurationList(AuthoritEventArgs args)
        {
            API.Configuration.LibraryConfigurationList libConfigList = new Authorit.API.Configuration.LibraryConfigurationList();
            switch (GetLibraryType(args))
            {
                case LibraryType.Unknown:
                    break;
                case LibraryType.Jet:
                    libConfigList.Add(GetJetLibraryConfiguration(args));
                    break;
                case LibraryType.Sql:
                    libConfigList.Add(GetSqlLibraryConfiguration(args));
                    break;
                default:
                    break;
            }
            return libConfigList;
        }

        /// <summary>
        /// The <see cref="GetLibraryType"/> static method gets the type of Author-it library specified by the <paramref name="args"/> parameter.
        /// </summary>
        /// <param name="args">An instance of the <see cref="AuthoritEventArgs"/> class that contains Author-it library information</param>
        /// <returns>One of the valid <see cref="LibraryType"/> values</returns>
        /// <remarks>
        /// The <see cref="GetLibraryType"/> method returns <see cref="LibraryType.Unknown"/> when the <paramref name="args"/> properties contain
        /// values for both <see cref="LibraryType.Jet"/> and <see cref="LibraryType.Sql"/> types
        /// or if the parameter contains null values for both types.
        /// </remarks>
        /// <seealso cref="GetLibraryConfigurationList(AuthoritEventArgs)"/>
        public static LibraryType GetLibraryType(AuthoritEventArgs args)
        {
            if (null == args.LibraryPath && (null == args.SqlServername || null == args.SqlDatabase))
            {
                return LibraryType.Unknown;
            }
            else if (null != args.LibraryPath && (null != args.SqlServername || null != args.SqlServername))
            {
                return LibraryType.Unknown;
            }
            else if (null != args.LibraryPath)
            {
                return LibraryType.Jet;
            }
            else if (null != args.SqlServername && null != args.SqlDatabase)
            {
                return LibraryType.Sql;
            }
            return LibraryType.Unknown;
        }

        /// <summary>
        /// The <see cref="GetSqlLibraryConfiguration(AuthoritEventArgs)"/> method gets a SQL Server library configuration using the specified arguments.
        /// </summary>
        /// <param name="args">The arguments for the event</param>
        /// <returns>A SQL Server library configuration</returns>
        /// <remarks>Use the <see cref="GetJetLibraryConfiguration(AuthoritEventArgs)"/> method when you know the library specified by <paramref name="args"/> is a <see cref="LibraryType.Sql"/> library. Use the <see cref="GetLibraryType(AuthoritEventArgs)"/> method to determine the library type.</remarks>
        public static API.Configuration.SqlServerLibraryConfiguration GetSqlLibraryConfiguration(AuthoritEventArgs args)
        {
            ValidateParameter(args, "args");
            Configuration.SqlServerLibraryConfiguration sqlConfig = new Authorit.API.Configuration.SqlServerLibraryConfiguration();
            if (null == args.SqlDatabase || null == args.SqlServername)
            {
                throw new ArgumentException("SqlDatabase and SqlServername cannot be null", "args");
            }

            sqlConfig.Database = args.SqlDatabase;
            sqlConfig.Server = args.SqlServername;
            sqlConfig.Alias = String.Format("{0} on {1}", args.SqlDatabase, args.SqlServername);

            if (!IsTrustedSqlConnection(args))
            {
                sqlConfig.TrustedConnection = false;
                sqlConfig.LoginID = args.SqlUsername;
                sqlConfig.Password = args.SqlPassword;
            }
            else
            {
                sqlConfig.TrustedConnection = true;
            }

            return sqlConfig;
        }

        public static bool SaveToXml(List<int> objectIds, AuthoritEventArgs args, string filePath, out Exception e)
        {
            e = new Exception();
            bool success = false;
            string objectsXml = GetObjectXml(objectIds, args);

            return success;
        }

        public static string GetObjectXml(int objectId, AuthoritEventArgs args)
        {
            string objectXml = String.Empty;
            Authorit.API.ApplicationContext appContext = GetAppContext(args);
            Authorit.API.Services.Authentication authService = appContext.GetServiceInstance<Authorit.API.Services.Authentication>();
            Authorit.API.Services.ObjectCommon objectCommon = appContext.GetServiceInstance<Authorit.API.Services.ObjectCommon>();
            Authorit.API.Dto.LoginResponse loginResponse = new Dto.LoginResponse();
            if (!authService.IsLoggedIn())
            {
                loginResponse = authService.Login(appContext.LibrariesConfiguration[0].Alias, args.Username, args.Password);
                switch (loginResponse.Result)
                {
                    case Authorit.API.Dto.LoginResult.FailureDatabaseNotFound:
                        break;
                    case Authorit.API.Dto.LoginResult.FailureError:
                        break;
                    case Authorit.API.Dto.LoginResult.FailureIncorrectUsernameOrPassword:
                        break;
                    case Authorit.API.Dto.LoginResult.FailureLibraryFutureVersion:
                        break;
                    case Authorit.API.Dto.LoginResult.FailureLibraryOpen:
                        break;
                    case Authorit.API.Dto.LoginResult.FailureLibraryUpgradeNeeded:
                        break;
                    case Authorit.API.Dto.LoginResult.FailureLicensing:
                        break;
                    case Authorit.API.Dto.LoginResult.Success:
                        objectXml = objectCommon.GetXml(objectId);
                        break;
                    default:
                        break;
                }
            }
            else
            {
                objectXml = objectCommon.GetXml(objectId);
            }
            return objectXml;
        }

        public static string GetObjectXml(List<int> objectIds, AuthoritEventArgs args)
        {
            StringBuilder xml = new StringBuilder();
            for (int i = 0; i < objectIds.Count; i++)
            {
                xml.Append(GetObjectXml(objectIds[i], args));
            }
            return xml.ToString();
        }



        public static Authorit.API.ApplicationContext GetAppContext(AuthoritEventArgs args)
        {
            Authorit.API.ApplicationContext appContext = new ApplicationContext(GetLibraryConfigurationList(args));
            return appContext;
        }

        /// <summary>
        /// The <see cref="GetTopicText(int, API.Services.Topic)"/> method gets the text of the specified topic.
        /// </summary>
        /// <param name="topicId">The object ID of the topic</param>
        /// <param name="topicService">The topic service to use</param>
        /// <returns>A string containing the text of the specified topic with HTML tags removed</returns>
        public static string GetTopicText(int topicId, API.Services.Topic topicService)
        {
            return htmlRegex.Replace(GetTopicTextHtml(topicId, topicService), string.Empty);
        }

        /// <summary>
        /// The <see cref="GetTopicTextHtml(int, API.Services.Topic)"/> method gets the HTML text of the specified topic.
        /// </summary>
        /// <param name="topicId">The object ID of the topic</param>
        /// <param name="topicService">The topic service to use</param>
        /// <returns>A string containing the HTML text of the specified topic</returns>
        public static string GetTopicTextHtml(int topicId, API.Services.Topic topicService)
        {
            string topicText = "";
            string xml = topicService.GetTopicTextXml(topicId);
            if (!String.IsNullOrEmpty(xml))
            {
                topicText = xml;
            }
            return topicText;
        }

        /// <summary>
        /// The <see cref="GetWordCount(string)"/> method gets the count of words in the specified string.
        /// </summary>
        /// <param name="text">The string containing words to count</param>
        /// <returns>The number of words in the specified string</returns>
        public static int GetWordCount(string text)
        {
            MatchCollection collection = Regex.Matches(text, @"[\S]+");
            return collection.Count;
        }

        /// <summary>
        /// The <see cref="GetWordCount(int, API.Services.Topic)"/> method gets the count of words in the specified topic.
        /// </summary>
        /// <param name="topicId">The object ID of the topic</param>
        /// <param name="topicService">The topic service to use</param>
        /// <returns>The number of words</returns>
        public static int GetWordCount(int topicId, API.Services.Topic topicService)
        {
            return GetWordCount(GetTopicText(topicId, topicService));
        }

        /// <summary>
        /// The <see cref="IsTrustedSqlConnection(AuthoritEventArgs)"/> static method returns a value indicating whether the specified SQL Server connection uses a trusted connection.
        /// </summary>
        /// <param name="args">An instance of the <see cref="AuthoritEventArgs"/> class that contains SQL Server connection information</param>
        /// <returns>true if the information specifies a trusted connection; otherwise false</returns>
        public static bool IsTrustedSqlConnection(AuthoritEventArgs args)
        {
            ValidateParameter(args, "args");
            bool trusted = false;

            if (null != args.SqlServername && null != args.SqlDatabase && null == args.SqlUsername)
            {
                trusted = true;
            }

            return trusted;
        }
        // Private Methods (1) 

        /// <summary>
        /// The <see cref="ValidateParameter(object, string)"/> method validates an object as not null.
        /// </summary>
        /// <param name="param">The parameter to validate</param>
        /// <param name="paramName">The name of the parameter</param>
        /// <remarks>The <see cref="PluginUtility"/> class uses the <see cref="ValidateParameter(object, string)"/> method
        /// to validate paramters passed to public methods.</remarks>
        private static void ValidateParameter(object param, string paramName)
        {
            if (null == param)
            {
                throw new ArgumentNullException(paramName);
            }
        }

        #endregion Methods 
    }
    #endregion
    #region LibraryType
    /// <summary>
    /// The <see cref="LibraryType"/> enumeration specifies the type of Author-it library.
    /// </summary>
    /// <remarks>The <see cref="PluginUtility.GetLibraryConfiguration(AuthoritEventArgs)"/>, <see cref="PluginUtility.GetLibraryConfigurationList(AuthoritEventArgs)"/>, and <see cref="PluginUtility.GetLibraryType(AuthoritEventArgs)"/> methods used the <see cref="LibraryType"/> enumeration when determing the type of library.
    /// <h4 class="dtH4">Implementation</h4>
    /// <code>
    /// public enum LibraryType
    /// {
    ///     Unknown = 0,
    ///     Jet = 1,
    ///     Sql = 2
    /// }
    /// </code>
    /// </remarks>
    /// <seealso cref="Authorit.API.ApplicationContext"/>
    public enum LibraryType
    {
        #region Enumeration values
        #region Unknown = 0
        /// <summary>
        /// Unknown or invalid library information
        /// </summary>
        Unknown = 0,
        #endregion
        #region Jet = 1
        /// <summary>
        /// Jet file library
        /// </summary>
        Jet = 1,
        #endregion
        #region Sql = 2
        /// <summary>
        /// SQL Server library
        /// </summary>
        Sql = 2
        #endregion
        #endregion
    }

    #endregion
}
